#include "tcp_receiver.hh"

// Dummy implementation of a TCP receiver

// For Lab 2, please replace with a real implementation that passes the
// automated checks run by `make check_lab2`.

template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {}

using namespace std;

bool TCPReceiver::segment_received(const TCPSegment &seg) {
    const TCPHeader header = seg.header();
    // process syn signal
    if (!_syn_set) {
        if (header.syn) {
            _isn = header.seqno;
            _ackno = _isn;
            _syn_set = true;
        } else {
            return false;  // hasn't syn and no header syn
        }
    } else {
        if (header.syn)  // has get syn, ignore others
            return false;
    }

    if (header.fin) {  // has get fin, ignore others
        if (!_fin_set) {
            _fin_set = true;
        } else {
            return false;
        }
    }


    uint64_t seg_seqno_start = unwrap(header.seqno, _isn, _checkpoint);
    uint64_t seqno_start = unwrap(_ackno, _isn, _checkpoint);
    if (!header.syn) {
        seg_seqno_start -= 1;
        seqno_start -= 1;
    }
    uint64_t seg_seqno_end = seg_seqno_start + seg.payload().size();
    uint64_t seqno_end = seqno_start + (window_size() ? window_size() - 1 : 0);
    if (seg.payload().size() == 0) {
        if (seg_seqno_start > seqno_end || seg_seqno_end < seqno_start) { //not fin and not syn and no payload
            return false;
        }
    } else {
        seg_seqno_end -= 1;
        if (seg_seqno_start > seqno_end || seg_seqno_end < seqno_start) {
            return false;
        }
        uint64_t pay_load_end = seg_seqno_end;
        if (pay_load_end > seqno_end) {
            pay_load_end = seqno_end;
        }
        _reassembler.push_substring(
            seg.payload().copy().substr(0, pay_load_end - seg_seqno_start + 1), seg_seqno_start, header.fin);
    }
    _checkpoint = _reassembler.stream_out().bytes_written();

    if (_fin_set && _reassembler.unassembled_bytes() == 0) {
        _reassembler.stream_out().end_input();
    }

    if (_reassembler.stream_out().input_ended()) {
        _ackno = wrap(_reassembler.stream_out().bytes_written() + 2, _isn);
    } else {
        _ackno = wrap(_reassembler.stream_out().bytes_written() + 1, _isn);
    }
    return true;
}

optional<WrappingInt32> TCPReceiver::ackno() const {
    if (_syn_set) {
        return _ackno;
    } else {
        return {};
    }
}

size_t TCPReceiver::window_size() const { return _capacity - _reassembler.stream_out().buffer_size(); }
